package naito_rescue.agent;

import java.util.*;
import java.io.*;

import rescuecore2.worldmodel.*;
import rescuecore2.standard.messages.*;
import rescuecore2.messages.*;
import rescuecore2.log.Logger;

import rescuecore2.standard.entities.*;
import rescuecore2.misc.geometry.*;
import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.object.*;
import naito_rescue.router.*;
import naito_rescue.task.*;
import naito_rescue.task.job.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;

/**
*  啓開隊だよ
*
*/
public class NAITOPoliceForce extends NAITOEnvironmentAgent<PoliceForce>
{
	private List<NAITOArea> clearAroundHeres;
	private List<StandardEntity> clearMembers;
	private double clearingWidth;
	private double clearingHeight;
	private double clearingStartX;
	private double clearingStartY;
	private List<Refuge> refugesOfTarget;
	private int    preTimeAddRefuge = -1; 
	
	@Override
	public String toString(){
		return "NAITOPoliceForce." + me().getID() + "";
	}

	@Override
	protected void postConnect(){
		super.postConnect();
        
        //
		clearMembers = new ArrayList<StandardEntity>();
		clearMembers.addAll(this.pfList);
		Collections.sort(clearMembers, ID_COMP);
		clearAroundHeres = new ArrayList<NAITOArea>();
		decideClearingArea();
		
		for(NAITOArea area : clearAroundHeres){
			this.currentTaskList.add(new ClearPathTask(this, area, 3000));
			//logger.info("Adding ClearPathTask(ID=" + area.getID() + ")", "PF#postConnect");
		}
		refugesOfTarget = new ArrayList<Refuge>();
		for(StandardEntity entity : allRefuges){
			Refuge refuge = (Refuge)entity;
			if(refuge.getX() > clearingStartX && refuge.getX() <= (clearingStartX + clearingWidth) &&
			   refuge.getY() > clearingStartY && refuge.getY() <= (clearingStartY + clearingHeight)){
			   	refugesOfTarget.add(refuge);
			}
		}
	}
	private void decideClearingArea(){
		int    roleID = clearMembers.indexOf(me());
		int    oldRoleID = roleID;
		int    separateBlock = 1;
		
		
		//DisasterSpaceをいくつのブロックに分割するかを決める
		for(; (separateBlock * separateBlock) < clearMembers.size();separateBlock++);

		separateBlock--;
		if(separateBlock < 1) separateBlock = 1;

		//roleIDの正規化...roleID=[0 ... pow(separateBlock,2)-1]になるように
		while(roleID >= (separateBlock * separateBlock)) roleID -= separateBlock;
		
		String context = "decideClearingArea";
		//logger.info("clearMembers.size() = " + clearMembers.size(), context);
		//logger.info("roleID = " + oldRoleID, context);
		//logger.info("separateBlock = " + separateBlock, context);
		//logger.info("Normalized roleID = " + roleID, context);
		//logger.info("w_minX = " + w_minX, context);
		//logger.info("w_minY = " + w_minY, context);
		//logger.info("w_maxX = " + w_maxX, context);
		//logger.info("w_maxY = " + w_maxY, context);
		//どっからどこまでのBuildingを探訪するか決定する
		clearingWidth  = (w_maxX - w_minX) / separateBlock;
		clearingHeight = (w_maxY - w_minY) / separateBlock;
		clearingStartX      = w_minX + clearingWidth * (roleID % separateBlock);
		clearingStartY      = w_minY + clearingHeight * (roleID / separateBlock);
		
		//logger.info("clearingWidth = " + clearingWidth, context);
		//logger.info("clearingHeight = " + clearingHeight, context);
		//logger.info("clearingStartX = " + clearingStartX, context);
		//logger.info("clearingStartY = " + clearingStartY, context);
		Building b = null;
		for(StandardEntity building : allBuildings){
			b = (Building)building;
			if(b.getX() > clearingStartX && b.getX() <= (clearingStartX + clearingWidth) &&
			   b.getY() > clearingStartY && b.getY() <= (clearingStartY + clearingHeight)){
				clearAroundHeres.add(allNAITOAreas.get(b.getID()));
				//logger.info("clearAroundHeres.add(ID=" + b.getID() + ")", context);
			}
		}
	}
	
	@Override
	protected void think(int time, ChangeSet changed, Collection<Command> heard){
		String context = "PF#think";
		super.think(time, changed, heard);
		if (time < config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			return;
		}else if(time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			
		}
		//目の前にある閉塞を啓開させる
		Area location = (Area)(getLocation());
		Blockade blockade = getTargetBlockadeNearMe();
		if(blockade != null){
			//logger.info("Clearing Blockade near me. = " + blockade, context);
			clear(blockade.getID());
			return;
		}
		
		//logger.info("doDefaultSearch -> Humanoid", context);
		
		/* フィルタリングされたHelpMeInBlockadeMessage or BlockedRoadMessageからタスクを追加する */
		addTaskByMessage(this.filteredMessages);
		updateTaskPriority();
		removeFinishedTask();
		currentTask = currentTaskList.peek(); //優先度の最も高いタスクを取得(閉塞している and 距離の最も近い道路ほど優先度が高い)
		if(currentTask != null){
			currentJob = currentTask.currentJob();
			if(currentJob != null){
				currentJob.act();
				return;
			}
		}
		doDefaultSearch();

	}
	protected void removeFinishedTask(){
		//logger.info("** removeFinishedTask(); **");
		for(Iterator<Task> it = currentTaskList.iterator();it.hasNext();){
			Task t = it.next();
			if(t.isFinished()){
				//logger.info("Remove finished task: " + t);
				it.remove();
			}
		}
		//logger.info("** removeFinishedTask(); end **");
	}
	Comparator<NAITOArea> naitoAreaDistanceSorter = new Comparator<NAITOArea>(){
		@Override
		public int compare(NAITOArea a1, NAITOArea a2){
			EntityID locationID = getLocation().getID();
			int distanceToFirst  = model.getDistance(a1.getID(), locationID);
			int distanceToSecond = model.getDistance(a2.getID(), locationID);
			return distanceToFirst - distanceToSecond;
		}
	};
	protected void addTaskByMessage(List<NAITOMessage> receivedNow){
		String context = "PF#addTaskByMessage";
		List<NAITOArea> targets = new ArrayList<NAITOArea>();
		List<EntityID> targetIDs = new ArrayList<EntityID>();
		for(NAITOMessage m : receivedNow){
			if(m instanceof BlockedRoadMessage){
				BlockedRoadMessage brm = (BlockedRoadMessage)m;
				if(brm.getIDs() != null){
					targetIDs.addAll(brm.getIDs());
				}
			}else if(m instanceof HelpMeInBlockadeMessage){
				HelpMeInBlockadeMessage hibm = (HelpMeInBlockadeMessage)m;
				if(hibm.getRoadID() != null){
					targetIDs.add(hibm.getRoadID());
				}
			}
		}
		for(EntityID id : targetIDs){
			NAITOArea targetArea = allNAITOAreas.get(id);
			if(targetArea != null){
				targets.add(targetArea);
			}
		}
		// sort.
		Collections.sort(targets, naitoAreaDistanceSorter);
		
		int task_priority = 7000;
		for(NAITOArea area : targets){
			addTaskIfNew(new ClearPathTask(this, area, task_priority));
			task_priority -= 500;
		}
	}
	/*
	protected void addTaskByMessage(List<NAITOMessage> receivedNow){
		String context = "PF#addTaskByMessage";
		
		for(NAITOMessage m : receivedNow){
			//logger.info("Process Message: " + m);
			if(m instanceof BlockedRoadMessage){
				BlockedRoadMessage brm = (BlockedRoadMessage)m;
				//logger.info("One BlockedRoadMessage has received. ");
				List<EntityID> ids = brm.getIDs();
				for(EntityID id : ids){
					NAITOArea nArea = allNAITOAreas.get(id);
					if(nArea != null){
						//logger.info("Add ClearPathTask(" + nArea + ").", context);
						if(nArea.getX() > clearingStartX && nArea.getX() <= (clearingStartX + clearingWidth) &&
						   nArea.getY() > clearingStartY && nArea.getY() <= (clearingStartY + clearingHeight)){
						   //logger.trace("Add ClearPathTask(" + nArea + ", 3200)", context);
							addTaskIfNew(new ClearPathTask(this, nArea, 3700));
						}else{
							//logger.trace("Add ClearPathTask(" + nArea + ", 3100)", context);
							addTaskIfNew(new ClearPathTask(this, nArea, 3300));
						}
					}
				}
			}else if(m instanceof HelpMeInBlockadeMessage){
				HelpMeInBlockadeMessage hmibm = (HelpMeInBlockadeMessage)m;
				//logger.info("One BlockedRoadMessage has received. ");
				EntityID id = hmibm.getRoadID();
				NAITOArea nRoad = allNAITOAreas.get(id);
				if(nRoad != null){
					//logger.info("Add ClearPathTask(" + nRoad + ").", context);
					if(nRoad.getX() > clearingStartX && nRoad.getX() <= (clearingStartX + clearingWidth) &&
					   nRoad.getY() > clearingStartY && nRoad.getY() <= (clearingStartY + clearingHeight)){
					   //logger.trace("Add ClearPathTask(" + nRoad + ", 4000)", context);
						addTaskIfNew(new ClearPathTask(this, nRoad, 4000));
					}else{
						//logger.trace("Add ClearPathTask(" + nRoad + ", 3100)", context);
						addTaskIfNew(new ClearPathTask(this, nRoad, 3500));
					}
				}
			}
		}
		//logger.info("@@@@ NAITOPoliceForce.addTaskByMessage(); end. @@@@");
	}
	*/
	protected void updateTaskPriority(){
		Object[] tasks = currentTaskList.toArray();
		currentTaskList.clear();
		for(Object t : tasks){
			Task task = (Task)t;
			task.updatePriority();
			currentTaskList.add(task);
		}
	}
	protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
		return EnumSet.of(StandardEntityURN.POLICE_FORCE);
    }
    
    /*
    public Blockade getTargetBlockade(Area area, int maxDistance) {
        //logger.info("Looking for nearest blockade in " + area);
        //logger.info("NAITOPoliceForce.getTargetBlockade(" + area + ", " + maxDistance + ")");
		if (!area.isBlockadesDefined()) {
            //logger.info("Blockades undefined");
			//logger.info("!area.isBlockadesDefined(); ==> return null;");
            return null;
        }
        List<EntityID> ids = area.getBlockades();
        // Find the first blockade that is in range.
        int x = me().getX();
        int y = me().getY();
        for (EntityID next : ids) {
            Blockade b = (Blockade)model.getEntity(next);
            double d = findDistanceTo(b, x, y);
            //logger.info("Distance to " + b + " = " + d);
            if (maxDistance < 0 || d < maxDistance) {
                //logger.info("In range");
				//logger.info("There is blockade.");
				//logger.info("" + b);
                return b;
            }
        }
        //logger.info("No blockades in range");
        return null;
    }
    */

}
